En el cuaderno anterior aprendiste a instalar R, RStudio y las
librerías que utilizaremos en durante este taller. También te explique
que siempre que veas una caja gris con código dentro de ella (como la
que hay a continuación), debes seleccionar el contenido, cortarlo y
pegarlo en el editor de RStudio y que debía ejecutarlo bien con la
combinación CTRL/CMD+⏎ (CTRL en Windows;
CMD en Mac) o haciendo clic en el icono Run que hay en la
parte superior derecha de la ventana del editor.
Vamosa comenzar estableciendo los parámetros básicos de esta sesión
# Establecer las opciones
options(stringsAsFactors = F)
options(scipen = 999)
options(max.print=100)
y cargando las librería que usaremos.
library(tidyverse)
library(tidytext)
library(quanteda)
library(stopwords)
Aunque R se creó para el análisis estadístico y la visualización de los resultados de esos análisis, también es útil para manejar datos textuales que podemos convertir en datos estadísticos y visualizarlos.
Un corpus clásico para enseñar los principios de la manipulación de textos con R es el State of the Union Address (SOTU), un informe anual que, desde 1790, presenta el Presidente de los Estados Unidos en un sesión conjunta del Congreso. Lo vamos a utilizar, aunque también podríamos hacer uso de los mensajes de Navidad que pronuncia el Jefe del Estado en de Nochebuena, e incluso los debates del Estado de la Nación o los Debates de Investidura, o cualquier otro conjunto (corpus) de datos que se quiera explorar.
All todos los SOTU se pueden conseguir en la red, hay varias fuentes
que los han publicado. Sin embargo, los he cosechado y he hecho una
pequeña labor de limpieza y están disponibles en un repositorio de
GitHub. Para cargarlos se puede utilizar la función
read_Lines cuyo primer argumento es el nombre del fichero
que contiene el texto (en este caso es una dirección —URL— de internet).
Minemos duranto un ratito el discurso de 2022.
Lo primero de todo es cargarlo en un objeto que
llamaremos last_sotu.
last_sotu <- read_lines(url("https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/242.txt"))
# Inspeccionemos el texto (solo verás la primera línea.)
str(last_sotu)
chr [1:101] "Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. "| __truncated__ ...
Una vez que lo hemos guardado en un objeto, es muy
sencillo extraer información acerca de la frecuencia de las palabras que
contiene y crear listas de palabras. Para lograrlo lo primero que hay
que hacer es utilizar la función unnest_tokens, que
dividirá el texto en palabras (tokens) y después usaremos la funcición
count para conocer las frecuencias absolutas de cada
palabra tipo.
Lo primero que hay que hacer es transformar ese extenso texto en una
especie de tabla con la función tibble. Esto hará que
podamos manejarlo con mayor facilidad.
tidy_sotu <- tibble(text = last_sotu)
# Inspeccionamos los datos (solo las 10 primeras entradas)
tidy_sotu
ATENCIÓN
Los nombres de los objetos los voy a
utilizar en inglés, pero nada impide que uses palabras de la lengua que
mejor que convenga. Solo hay unas pequeñas limitaciones: 1) no pueden
empezar por un número; 2) distingue entre mayúsculas y minúsculas —no es
lo mismo Ciudad que ciudad que
CIUDAD son tres nombres nombre diferentes— y 3) hay una
serie de caracteres que no se pueden utilizar (los símbolos matemáticos,
las barras, y algunos otros).
Y una recomendación: no uses letras con
tildes, pueden ser fuente de tremendos quebraderos de cabeza.
Cada línea de esta tabla corresponde a un párrafo del discurso, es decir, incluye todo el texto que hay antes de cada golpe de la tecla ⏎. Ahora lo podemos dividir en tokens.
Siempre que veas una tabla con la de antes, puedes recorrerla
haciendo clic en el Next que hay en la parte inferior de la
misma.
tidy_words <- tidy_sotu %>%
unnest_tokens(word, text) %>%
count(word, sort=T)
# Inspeccionamos los datos (solo las 10 primeras entradas)
tidy_words
Lo que le hemos dicho a R es que divida el texto que hay en el
objeto tidy_sotu en palabras (tokens), que
las cuente, las ordene de mayor a menor frecuencia (n) y
las guarde en otro object llamado tidy_words.
Fíjate que en el margen inferior, en el lado izquierdo dice
1-10 of 1.735 rows. Esto quiere decir que el texto del
discurso de 2022 tiene 1735 palabras diferentes (palabras tipo),
mientras que la columna n indica cuentas veces aparece cada
una de las palabras tipo.
Tan fácil como dividir el texto en palabras individuales, es el
extraer los n-grams, es decir, las secuencias de n
palabras consecutivas. Es sencillo porque la función
unnest_tokens tiene un argumento llamado token
por medio del cual le puedes indicar a R que quieres extraer
ngrams. Si es lo que te interesa, entonces debes
especificar el número de palabras que que ha de agrupar; para ello se
utiliza el argumento n. Con el código que hay a
continuación le pides a R que quieres extraer todos los grupos de cuatro
palabras (4-grams) y que informe de la frecuencia absoluta y que las
presente en orden decreciente.
tidy_sotu %>%
unnest_tokens(word,
text,
token = "ngrams",
n = 4) %>%
count(word, sort=T)
También es posible dividir el texto en oraciones (no enredemos con
los coneptos de oración, aquí funcionan de otra manera) en vez de en
palabras. De nuevo es fácil de hacer porque el argumento
tokens de la función unnest_tokens puede tener
el valor sentences.
tidy_sotu %>%
unnest_tokens(sentences,
text,
token = "sentences")
ATENCIÓN
Cuando se le pide que divida cualquier texto en
palabras, n-grams u oraciones, una de las cosas que hace es
convertir las mayúsculas en minúsculas de manera que First en
First Lady y la de cualquier caso de first que pueda
aparecer a lo largo del texto las cuente como la misma palabra tipo. Sé
que esto es problemático en algunos casos, pero hay herramientas para
solucionarlo.
Ya hemos visto que en el discurso de Biden de 2022 había 1735
palabras diferentes (palabras tipo), y vimos que cada una de ella
aparece un número determinados de veces (the aparece 300 veces,
and 264 y to 234), pero no sabes cuántas son las
palabras que conforman el discurso. Hay 6553 palabras
token. La forma de calcularlo es sumando todos los valores de
n del objeto tidy_words y se consigue con la
función sum.
sum(tidy_words$n)
[1] 6553
Al dividir el texto en tokens, has visto que palabras tan usuales como “the”, “to”, “and”, “of” y “we” están en lo alto de la tabla. Estas palabras poco pueden decir del contenido del discurso (en otros tipos de análisis estas palabras son oro molido). Vas a borrarlas.
data(stop_words)
tidy_words %>%
anti_join(stop_words)
A pesar de haber elminado todas las palabras de función o palabras vacías hay, sin embargo, algunas palabras muy corrientes y contracciones como “let’s”, “that’s”, “it’s” o “we’re”. Quizá te interese localizar todas aquellas palabras que se usan con muchísima más frecuencia en este texto de lo que se utilizan en un basto corpus del inglés. Para conseguirlo, necesitas un dataset que recopile estas frecuencias. Uno muy bueno para eso es el de Peter Norvig’s que se basa en el Google Web Trillion Word Corpus, que se ha construido con textos extraidos de sitios web en inglés.
word_frequencies <- read_csv("https://raw.githubusercontent.com/programminghistorian/jekyll/gh-pages/assets/basic-text-processing-in-r/word_frequency.csv")
head(word_frequencies)
La primera columna indica la lengua de que se trata (es siempe “en” = inglés), la segunda presenta las palabras y la tercera ofrece el porcentaje de uso de cada palabra en el Trillion Word Corpus.
Para combinar todas estas frecuencias con la tabla en la que tienes
con los datos del discurso de Biden de 2022, usarás la función
inner_join. esta función toma dos tablas (o dos dataset) y
los combina en una sola en virtud de las columnas que tengan el mismo
nombre; en este caso la columna común es la llamada
word.
tidy_words %>%
inner_join(word_frequencies) %>%
filter(frequency < 0.1)
Esta lista ya empieza a tener un aspecto mejor. Términos como “america”, “american(s)”, “year”, “people” y “world” han escalado hasta la parte superior de la tabla y ya podemos especular que aparecen con muchas frecuencia en discursos de políticos, como es el caso del SOTU, pero su ocurrencia es relativamente menor en otros dominios. Si establecieras un umbral mucho más bajo, digamos del 0.02, podrás ver un resumen mucho más interesante del discurso.
tidy_words %>%
inner_join(word_frequencies) %>%
filter(frequency < 0.02)
Si haces clic en el Next de la parte baja de la tabla
anterior, verás que aparecen algunos términos interesantes como “putin”,
“ukrainian”, “allies”, “pandemic”, “deficit” e “inflation”. Estos
parecen ser algunos de los temas del discurso.
Antes de meterte en agua más profundas, vas a ver un breve resumen basado en las cinco palabras más frecuentes de este discurso. para conseguirlo, necesitas otra tabla con una serie de metadatos que te permitan identificar totalmente cada uno de los discursos. Esta tabla contiene el nombre del Presidente, el año del discurso, años en los que estuvo en la Casa Blanca (algo realmente irrelevante), partido al que pertenece y tipo de discurso, pues pueden escritos o hablados. Vas a cargarlo en tu ordenador.
sotu_meta <- read_tsv("https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu_meta.txt")
# Muestra el contenido (solo el principio)
sotu_meta
Ya es hora de resumirlo en una sola línea. Al final de la instrucción
hay un 5, lo cual implica que seleccionará las cinco
palabras más utilizadas, se puede incrementar a la cantidad que quieras,
pero un sumario de más de 10 palabras quizá sea muy poco
informativo).
address_summary <- tidy_words %>%
inner_join(word_frequencies) %>%
filter(frequency < 0.02)
result <- c(sotu_meta$president[242],
sotu_meta$year[242],
address_summary$word[1:5])
paste(result, collapse = "; ")
[1] "Joe Biden; 2022; americans; tonight; families; pass; costs"
Creo que se podía haber eliminado la palabra “tonight”, no dice nada del tema, tan solo que lo leyó una tarde noche.
Crear unas concordancias o tablas de palabras clave en contexto
key-word-in-context (KWIC) es uno de los procedimientos más
usuales cuando se analizan datos textuales. No tienes que esforzarte
mucho para conseguirlo, hay varias funciones que lo harán con sencillez.
Utilizará la función kwic de la librería
quanteda para crear una concordancia KWIC, pero exige
dividir el texto en tokens con la función tokens, pero de
la propia librería quanteda.
kwic_multiple <- last_sotu %>%
tokens() %>%
kwic(pattern = "Ukraine",
window = 3) %>%
as.data.frame()
# Inspecciona el comienzo de la tabla
head(kwic_multiple)
El primer paso para analizar todos el corpus de los discursos del
State of the Union es hacer que R los lea todos a la vez. Esto
implica utilizar, como has hecho antes, la función
readLines, pero unirás todos los párrafos de cada uno de
los discursos con la función paste, añadirás un sistema de
identificación para saber qué discurso es cada uno de ellos. Todo esto
se tiene que hacer por medio de un bucle, porque quieres meter nada más
y nada menos que 244 discursos en una única tabla (u objeto) llamada
all_sotu.
Puesto que los ficheros se encuentran en la web, el primer paso es
declarar la url completa de cada discurso y crear una tabla vacía
(all_sotu) para guardar todos los teextos.
La instrucción para crear los nombrs de los ficheros es un proceso
que requiere dos pasos. En primer lugar, se ha de guardar (almacenar) la
parte fija de la URL en el objeto base_url. Y, en
segundo lugar, hay que crear los nombres de los ficheros que están
construidos por un número entre 1 y 244. Está
es, aparentemente, la parte más complicadilla, pues la secuencia de
1 a 244 debe estar precedido por uno (para los
números 10 a 99) o dos (para los números
entres el 1 y el 9) ceros. La función ideal
para esto es sprintf, que permite una fórmula para añadir
los números con los ceros a la izquierda, en nuestro caso entre
001 … 099… 244, que incorpore la
extensión .txt y que la añada al final de la parte fija de
la url.
base_url <- "https://raw.githubusercontent.com/7PartidasDigital/MLex/master"
files <- sprintf("%s/sotu/%03d.txt", base_url, 1:244)
all_sotu <- NULL
Quizá te estés preguntado qué aspecto tienen las URL que acabas de
crear. Vas a echarle una ojeada a las seis primeras con la función
head.
head(files)
[1] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/001.txt"
[2] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/002.txt"
[3] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/003.txt"
[4] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/004.txt"
[5] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/005.txt"
[6] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/006.txt"
y las seis últimas con la función tail.
tail(files)
[1] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/239.txt"
[2] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/240.txt"
[3] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/241.txt"
[4] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/242.txt"
[5] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/243.txt"
[6] "https://raw.githubusercontent.com/7PartidasDigital/MLex/master/sotu/244.txt"
Ahora ya puedes leer todos los discursos e incorporarlos a la tabla (le llevará unos dos minutos o un poquito más.)
for (i in 1:length(files)) {
sotu <- readLines(files[i])
sotu <- paste(sotu, collapse = "\n")
temporary <- tibble(speech = i,
text = sotu)
all_sotu <- bind_rows(all_sotu, temporary)
}
Además, le vaas a añadir los metadatos que tienes guardados en
sotu_meta:
all_sotu <- full_join(all_sotu, sotu_meta, by="speech")
ya tienes todos los elementos que necesitas para analizar todos los
discursos SOTU de los últimos 244 años. Lo primero es dividirlos en
tokens. Ya has visto que ña función unnest_tokens los puede
hacer en poquísimo tiempo.
all_words <- all_sotu %>%
unnest_tokens(word, text)
all_words
Tienes al alcance de los dedos un corpus de poco más de 2.000.000 de palabras (exactamente 2.017.504 tokens). Ahora puedes plantearte muchas preguntas como ¿Hay un patrón temporal que pueda explicar la diferente extensión de cada discurso? ¿Cómo se compara la extensión de los discursos de las últimas administraciones con los de Franklin D. Roosevelt, Abraham Lincoln y George Washington?
La mejor manera para observar esto es con un gráfico de dispersión.
Se puede dibujar con la función ggplot, para ello pondrás
el año en el eje X (el horizontal) y la longitud, entendida como el
número de palabras o tokens n en el eje vertical (o
y).
all_words %>%
group_by(year) %>%
count() %>%
ggplot() +
geom_point(aes(year,
n))
Parece ser que entre 1790 y 1850 la longitud de los discursos aumentó
progresivamente la longitud de los discursos, durante unos pocos anos
decreción, y volvieron a crecar hasta finales del siglo XIX. La
extensión disminuyó drásticamente en la época de la I Gerra Mundial,
aunque a lo largo del siglo XX hay una puñado de discursos muy extensos.
Hacia 2001 disminuyeron y comenzaron a alargarse de nuevo. ¿Hay alguna
razón para este vaivén en las longitud de los discursos. Has visto que
algunos presidentes predentaron sus discursos por escrito mientras que
otros los leyeron. ¿Es esta la razón? Puedes averiguarlo en segundos.
Para ello vas a emplear el argumento color para que los
coloree de acuerdo con el valor de la variable
sotu_type.
all_words %>%
group_by(year) %>%
count() %>%
left_join(sotu_meta) %>%
ggplot() +
geom_point(aes(year,
n,
color = sotu_type))
Pues eso parece. El incremento que se da a lo largo del siglo XIX se debe a que se pasó de un discurso oral a otro escrito. El cambio radical se produjo cuando cuando Woodrow Wilson (en 1913) volvió al origen, y lo hizo oralmente en el Congreso de Estados Unidos. Como puedes ver, los outliers (los más largos) se presentaron todos por escrito tras la Segunda Guerra Mundial.
Otra pregunta que te puedes hacer es si el pertenecer a uno u otro
partido tiene alguna influencia en la extensión de los discursos. Lo
puedes averiguar en segundos con tan solo cambiar con cambiar que el
argumento color use party en vez de
sotu_type.
all_words %>%
group_by(year) %>%
count() %>%
left_join(sotu_meta) %>%
ggplot() +
geom_point(aes(year,
n,
color = party))
No parece que el ser de de uno u otro partidos tenga nada que ver con la extensión. Lo más curioso es que desde finales de la década de 1990 los discursos más extensos fueron de presisdentes Demócratas.
Como veís, hasta ahora solo nos hemos planteado preguntas que se podrían hacer un estudiante de políticas o de historia, pero podemos hacernos otras que interesen a otros especialistas.
Has visto que puedes dividir el texto en oraciones. la longitud de estas puede ser un posible rasgo estilo, e incluso puede hablar de la complejidad del texto. Así que vamos a dividirlos en oraciones y contar cuántas palabras tiene cada uno de ellos.
all_sentences <- all_sotu %>%
unnest_tokens(sentence,
text,
token = "sentences") %>%
mutate(NumberWords = str_count(sentence,
pattern = "\\w+"))
Calculemos la mediana y vamos a verla un una gráfica, es más claro que una tabla con 244 líneas de números.
all_sentences %>%
group_by(year) %>%
mutate(median = median(NumberWords)) %>%
left_join(sotu_meta) %>%
ggplot() +
geom_point(aes(year,
median))
La gráfica muestra que, a lo largo del tiempo, hay una tendencia general a usar oraciones cada vez más cortas. Recuerda que algunos de los discursos tras la Segunda Guerra Mundial fueron escritos, como la gran mayoría de los del siglo XIX. Está claro que a mayor extensión del discurso, a pesar de estar escrito, nada tiene que ver con la longitud de las oraciones. Es evidente que los discursos de los presidentes nortamericanos han tendido a usar oraciones cada vez más cortas.
Para verlo de una manera más evidente, es posible incorporar una
línea que marque la tendencia con la función geom_smooth.
Este tipo de línea son de gran ayuda en muchas gráficas. Tienen un doble
proósito: marcan la tendencia general a lo largo de una serie temporal y
muestran con mayor claridad los datos atípicos.
all_sentences %>%
group_by(year) %>%
mutate(median = median(NumberWords)) %>%
left_join(sotu_meta) %>%
ggplot(aes(x = year,
y = median)) +
geom_point() +
geom_smooth()
¡AVISO! Es cierto que hay una tendencia general a acortar las oraciones, pero hay un pequeño problema que no he considerado y que podría tener cierta influencia en los resultados finales, por lo que debéis tenerlo en cuenta. El toquenizador, es decir, las instrucciones que dividen el texto en oraciones considera que el límite de las oraciones son uno de estos tres casos: puntos, signo de admiración y signos de interrogación. A lo largo de estos textos hay unas cuantas abreviaturas, como Mr., como se puede ver en esta tabla.
all_words %>%
filter(str_detect(word, regex("\\bmr\\b", ignore_case = TRUE))) %>%
select(word)
Has visto los bigramas más frecuentes de cada uno de los presidentes, pero no son nada ilustrativos, pues no ofrecen las palabras más interesantes de cada uno de ellos.
Vas a analizar los discursos de cada presidente, pero no desde el punto de vista de los discrusos individuales, sino del conjunto de cada uno de ellos.La talba que hay a continuación te mostrará que la palabra más frecuente de Roosevelt es el artículo “the” y la seguna más utilizada es la preposición “of”. Son tan corrientes que no tienen ningún interés.
all_words_by_president <- all_sotu %>%
unnest_tokens(word, text) %>%
count(president, word, sort = T)
# inspect data (top 10 sorted)
all_words_by_president
Sin embargo, hay una manera de localizar las palabras más importantes
de cada subcorpus (serie de discursos pronunciados por cada presidente)
y se consigue comparándolos entre sí. Las mátemáticas que hay tras esto
son de infarto, pero una de las cosas admirables de R y de todas las
librería que existen es que ha habido quienes se han ocupado de
ofrecernos funciones sencillas de usar. Uno de los métodos para extraer
las palabras mñas importantes o característica de cada presidente (o de
cualquier otro texto) es el llamado Term Frequency–Inverse Document
Frequency, o tf-idf para abreviar. Es una análisis estadístico que
calcula cuáles son las palabras clave de un texto y, por lotanto,
reflejan cuán características son dentro de un texto dado. El Term
Frequency–Inverse Document Frequency calcula la frecuencia de cada
palabra de un texto y la compara con la frecuencia que ofrece en los
documentos en los que aparece. la explicación no es muy clara, pero cómo
hallar los valores, y por tanto las palabras, es algo sencillo gracias a
la función bind_tf_idf.
all_words_by_president %>%
bind_tf_idf(word, president, n) %>%
mutate(president = factor(president,
levels = c("George Washington", "John Adams", "Thomas Jefferson",
"James Madison", "James Monroe", "John Quincy Adams",
"Andrew Jackson", "Martin Van Buren", "John Tyler",
"James K. Polk", "Zachary Taylor", "Millard Fillmore",
"Franklin Pierce", "James Buchanan", "Abraham Lincoln",
"Andrew Johnson", "Ulysses S. Grant", "Rutherford B. Hayes",
"Chester A. Arthur", "Grover Cleveland", "Benjamin Harrison",
"William McKinley", "Theodore Roosevelt", "William Howard Taft",
"Woodrow Wilson", "Warren G. Harding", "Calvin Coolidge",
"Herbert Hoover", "Franklin D. Roosevelt", "Harry S Truman",
"Dwight D. Eisenhower", "John F. Kennedy", "Lyndon B. Johnson",
"Richard M. Nixon", "Gerald R. Ford", "Jimmy Carter",
"Ronald Reagan", "George Bush", "William J. Clinton",
"George W. Bush", "Barack Obama", "Donald Trump", "Joe Biden"))) %>%
group_by(president) %>%
slice_max(tf_idf, n = 10) %>%
ungroup() %>%
mutate(word = reorder(word, tf_idf)) %>%
ggplot(aes(tf_idf, word, fill = president)) +
geom_col(show.legend = FALSE) +
labs(x = "tf-idf", y = NULL) +
facet_wrap(~president, ncol = 4, scales = "free")
Hay demasiados cifras, pueden que tengan algún significado, pero son
un sinsentido para nosotros. Así que borrémoslos. Para hacerlo hay que
rehacer parte del trabajo. Hay que dividir en palabras todos los
dicursos, pero lo primero que hay que hacer es borrar todos los números
con la función mutate.
all_words_by_president <- all_sotu %>%
mutate(text = str_remove_all(text, "[:digit:]")) %>%
unnest_tokens(word, text) %>%
count(president, word, sort = T)
Una vez hecho, hay que redibujar el gráfico. No hay que hacer nada nuevo.
all_words_by_president %>%
bind_tf_idf(word, president, n) %>%
mutate(president = factor(president,
levels = c("George Washington", "John Adams", "Thomas Jefferson",
"James Madison", "James Monroe", "John Quincy Adams",
"Andrew Jackson", "Martin Van Buren", "John Tyler",
"James K. Polk", "Zachary Taylor", "Millard Fillmore",
"Franklin Pierce", "James Buchanan", "Abraham Lincoln",
"Andrew Johnson", "Ulysses S. Grant", "Rutherford B. Hayes",
"Chester A. Arthur", "Grover Cleveland", "Benjamin Harrison",
"William McKinley", "Theodore Roosevelt", "William Howard Taft",
"Woodrow Wilson", "Warren G. Harding", "Calvin Coolidge",
"Herbert Hoover", "Franklin D. Roosevelt", "Harry S Truman",
"Dwight D. Eisenhower", "John F. Kennedy", "Lyndon B. Johnson",
"Richard M. Nixon", "Gerald R. Ford", "Jimmy Carter",
"Ronald Reagan", "George Bush", "William J. Clinton",
"George W. Bush", "Barack Obama", "Donald Trump", "Joe Biden"))) %>%
group_by(president) %>%
slice_max(tf_idf, n = 10) %>%
ungroup() %>%
mutate(word = reorder(word, tf_idf)) %>%
ggplot(aes(tf_idf, word, fill = president)) +
geom_col(show.legend = FALSE) +
labs(x = "tf-idf", y = NULL) +
facet_wrap(~president, ncol = 4, scales = "free")
Ahora los gráficos tienen algo más de sentido y depende de ti la interpretación. Hay una serie de problemas que debes tener en cuenta . Si te fijas en el gráfico de Kennedy verás que aparecen “viet” y “nam”, pero son una sola palabra “viet-nam”. Lo mismo sucede en el de Bush Jr. “al-Qaida” se ha dividido en los segmentos “al” y “qaida”. Por lo tanto, cuando uses estas técnicas para minar textos, tienes que tener en cuenta los problemas que puede haber escondidos en los textos.
Según Altenberg (1991: 128), “Aproximadamente el 70% de las palabras que constituyen un corpus forman parte de combinaciones de palabras usuales”. La investigación de tales combinaciones de palabras en corpus se remonta a los primeros estudios de Firth (1957) sobre colocaciones, quien resumió este principio con una sucinta frase: “reconocerás una palabra por la compañía que tiene”.
En esta sección vas a ver todas las colocaciones que ofrecen los discursos presidenciales. Ya has visto que se puede dividir un textos en tokens, pero también en n-grams, es decir, secuencias de n-palabras o n-tokens). Dividamos todos los discursos en bigramas, en secuencias de dos palabras / tokens.
all_bigrams <- all_sotu %>%
unnest_tokens(bigram,
text,
token = "ngrams",
n = 2)
# inspect data (top 10 sorted)
all_bigrams %>%
count(bigram, sort = T)
Como ya has visto, el resultado no es muy ilustrativo. De nuevo son
las palabras gramaticales —preposiciones, artículos, conjunciones…— las
que aparecen en primer lugar. La única excepción, esperable, es
United States. Podríamos estar tentados de usar la función
anti_join() para borrarlas. Sin embargo, no lo puedes hacer
directamente puesto que la lista de palabras vacías, o
stopwords que has cargado solo contienen unigramas, no hay ni
un solo bigramas, por lo que no funcionaría.
La solución se, una vez aue tenemos los bigramas, separar los dos constituyentes y borrar las palabraas vacías. Aunque se puede hacer todo con un solo bloque de código, lo voy a presentar en varios pasos, para que puedas ver la lógica del procedimiento.
En primer lugar vasa separar los dos elementos con la función
separate() y los vas a conservar en dos caloumnas que
llamaremos word1 y word2.
separated_bigrams <- all_bigrams %>%
separate(bigram,
c("word1", "word2"),
sep = " ")
# inspect data (top 10 sorted)
separated_bigrams %>%
count(word1, word2, sort = T)
Tiene casi el mismo aspecto que la tabla que se imprimió cuando dividimos los texto en bimagras, pero cada una de las partes está en una columna (variable) diferente.
El siguiente paso es borrar todas las palabras vacías, pero no puedes
usar la función anti_join. Vas a extraer todas las palabras
vacías con %in%, tanto de la columna word1
como de word2, y que no se encuentren la columna
–!– en la columna word dle objeto
stop_words. es un galimatías, lo sé.
filtered_bigrams <- separated_bigrams %>%
filter(!word1 %in% stop_words$word,
!word2 %in% stop_words$word)
# inspect data (top 10 sorted)
filtered_bigrams %>%
count(word1, word2, sort = T)
Aunque te lo pueda parecer, no estás viendo bigramas, tan solo estás
viendo dos palabras contiguas en dos columanas diferentes. Hay que
reconstruir los bimagras y se consigue con la función
unite(), que reune en una sola columna los valores que haya
en dos o más columnas.
united_bigrams <- filtered_bigrams %>%
unite(bigram, word1, word2, sep = " ")
# inspect data (top 10 sorted)
united_bigrams %>%
count(bigram, sort = T)
Ahora podemos trazar un gráfico que nos permita ver qué bigramas utiliza con mayor frecuencia cada presidente. Le llevará un poco de tiempo dibujarlo ().
united_bigrams %>%
mutate(president = factor(president,
levels = c("George Washington", "John Adams", "Thomas Jefferson",
"James Madison", "James Monroe", "John Quincy Adams",
"Andrew Jackson", "Martin Van Buren", "John Tyler",
"James K. Polk", "Zachary Taylor", "Millard Fillmore",
"Franklin Pierce", "James Buchanan", "Abraham Lincoln",
"Andrew Johnson", "Ulysses S. Grant", "Rutherford B. Hayes",
"Chester A. Arthur", "Grover Cleveland", "Benjamin Harrison",
"William McKinley", "Theodore Roosevelt", "William Howard Taft",
"Woodrow Wilson", "Warren G. Harding", "Calvin Coolidge",
"Herbert Hoover", "Franklin D. Roosevelt", "Harry S Truman",
"Dwight D. Eisenhower", "John F. Kennedy", "Lyndon B. Johnson",
"Richard M. Nixon", "Gerald R. Ford", "Jimmy Carter",
"Ronald Reagan", "George Bush", "William J. Clinton",
"George W. Bush", "Barack Obama", "Donald Trump", "Joe Biden"))) %>%
count(president, bigram, sort = T) %>%
group_by(president) %>%
top_n(5) %>%
ggplot() +
geom_col(aes(y = n , x = reorder(bigram,n)),
fill = "maroon") +
coord_flip() +
facet_wrap(~ president, ncol = 4, scales = "free")
Hay muchas otras posibilidades, como el análisis morfológico (Part-of-Speech (PoS) tagging) que permite identificar a qué clase de palabras pertenece cada una de ellas (p. e., sustantivo, adjectivo, verbo, etc.). Lo que se consigue es añadir a cada palabra del texto la etiqueta gramatical. Pero no tenemos tiempo. Lo único que me importa es que que este taller os haya despertado el interés por hacer minería de textos con el uso de R. Las posibilidades son enormes. Sé que lo he limitado a textos en inglés, pero puedes usar la lengua que te interese.
¡RECUERDA!
sessionInfo()
NOTE
Este cuaderno se basa en algunos tutoriales de Ladal, Programming
Historian y CuentaPalabras.
Para
quienes tengan intereses lingüísticos, recomiendo los tutoriales Ladal.